#include "scrambled_symbols.h"
#include "mytypes.h"
#include "hash.h"
#include <openssl/md5.h>
#include <string.h>
#include <stdio.h>

#if defined(__GNUC__)
#include <unistd.h>
#include <inttypes.h>
#endif


typedef union {
  struct {
    tUInt32 msb;
    tUInt32 lsb;
  } part;
  tUInt64 whole;
} tQuadWord;

tUInt32 hash6432shift(const unsigned char *str, tUInt32 size)
{
  tUInt64 key;

  key=*((tUInt64 *) str);

  key = (~key) + (key << 18); /* key = (key << 18) - key - 1; */
  key = key ^ (key >> 31);
  key = key * 21; /* key = (key + (key << 2)) + (key << 4); */
  key = key ^ (key >> 11);
  key = key + (key << 6);
  key = key ^ (key >> 22);
  return (tUInt32) key;
}


tUInt32 RSHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 b    = 378551;
   tUInt32 a    = 63689;
   tUInt32 hash = 0;
   tUInt32  i;

   for(i = 0; i < size; i++)
   {
      hash = hash * a + str[i];
      a    = a * b;
   }
#if defined(Linux)
   return (hash+ gethostid());
#else
   return (hash);
#endif
}
/* End Of RS Hash Function */


tUInt32 JSHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 1315423911;
   tUInt32  i;
   tByte *pstr;

   for(i=0, pstr=(tByte *) &str[0]; i < size; i++,pstr++)
   {
      hash ^= ((hash << 5) + (*pstr) + (hash >> 2));
   }

   return hash;
}
/* End Of JS Hash Function */


tUInt32 PJWHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 BitsInUnsignedInt = (tUInt32)(sizeof(tUInt32) * 8);
   tUInt32 ThreeQuarters     = (tUInt32)((BitsInUnsignedInt  * 3) / 4);
   tUInt32 OneEighth         = (tUInt32)(BitsInUnsignedInt / 8);
   tUInt32 HighBits          = (tUInt32)(0xFFFFFFFF) << (BitsInUnsignedInt - OneEighth);
   tUInt32 hash              = 0;
   tUInt32 test              = 0;
   tUInt32  i;

   for(i = 0; i < size; i++)
   {
      hash = (hash << OneEighth) + str[i];

      if((test = hash & HighBits)  != 0)
      {
         hash = (( hash ^ (test >> ThreeQuarters)) & (~HighBits));
      }
   }

   return hash;
}
/* End Of  P. J. Weinberger Hash Function */


tUInt32 ELFHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 0;
   tUInt32 x    = 0;
   tUInt32  i;
   
   for(i = 0; i < size; i++)
   {
      hash = (hash << 4) + str[i];
      if((x = hash & 0xF0000000L) != 0)
      {
         hash ^= (x >> 24);
      }
      hash &= ~x;
   }

   return hash;
}
/* End Of ELF Hash Function */


tUInt32 BKDRHash(const unsigned char *str, tUInt32 size)
{
  tUInt32 seed = 131; /* 31 131 1313 13131 131313 etc.. */
   tUInt32 hash = 0;
   tUInt32  i;

   for(i = 0; i < size; i++)
   {
      hash = (hash * seed) + str[i];
   }

   return hash;
}
/* End Of BKDR Hash Function */


tUInt32 SDBMHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 0;
   tUInt32  i;

   for(i = 0; i < size; i++)
   {
      hash = str[i] + (hash << 6) + (hash << 16) - hash;
   }

   return hash;
}
/* End Of SDBM Hash Function */


tUInt32 DJBHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 5381;
   tUInt32  i;
   for(i = 0; i < size; i++)
   {
      hash = ((hash << 5) + hash) + str[i];
   }

   return hash;
}
/* End Of DJB Hash Function */


tUInt32 DEKHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = size;
   tUInt32  i;
   for(i = 0; i < size; i++)
   {
      hash = ((hash << 5) ^ (hash >> 27)) ^ str[i];
   }

   return hash;
}
/* End Of DEK Hash Function */


tUInt32 BPHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 0;
   tUInt32  i;
   for(i = 0; i < size; i++)
   {
      hash = hash << 7 ^ str[i];
   }

   return hash;
}
/* End Of BP Hash Function */


tUInt32 FNVHash(const unsigned char *str, tUInt32 size)
{
   const tUInt32 fnv_prime = 0x811C9DC5;
   tUInt32 hash = 0;
   tUInt32  i;
   for(i = 0; i < size; i++)
   {
      hash *= fnv_prime;
      hash ^= str[i];
   }

   return hash;
}
/* End Of FNV Hash Function */


tUInt32 APHash(const unsigned char *str, tUInt32 size)
{
   tUInt32 hash = 0;
   tUInt32  i;
   for(i = 0; i < size; i++)
   {
      hash ^= ((i & 1) == 0) ? (  (hash <<  7) ^ str[i] ^ (hash >> 3)) :
                               (~((hash << 11) ^ str[i] ^ (hash >> 5)));
   }

   return hash;
}

tUInt32 MyHash(const unsigned char *str, tUInt32 size)
{
  unsigned char buf[20];
  unsigned  int *p=(tUInt32 *) &buf[0];
  tUInt32 n;
  tQuadWord l;
  char index_char[16];
  tUInt32 i;

  strncpy(index_char,(const char *) str,size);

  index_char[16]='\0';

  sscanf(index_char,"%llu", (tUInt64 *) &l); /* Ugly "warning: ISO C90 does not support the `ll' scanf length modifier" 
						message I don't know how to fix. */
  
  /*
    SHA1 hash is computed on 20 bytes.
  */
  /*  SHA1(index,size,buf); */

  /*
    MD5 hash is computed on 16 bytes.
  */
  MD5(str,size,buf);
  
  /* 
     We want to obtain an 32 bits integer from the computed hash.
     In order to take into account all the byte of the SHA1 sum,
     we add together the bytes of same rank. A rank is defined 
     by 'array index % 4'.
  */
  for (i=0; i<4; i++)
    {
      buf[i]=(char) (buf[0]+buf[i+4]+buf[i+8]+buf[i+12]);
    }

  /* 
     Getting current hostid that we will added into the
     computing of the index (just adding the value together). 
  */
#if defined(Linux)
  n=(tUInt32) gethostid() + l.part.msb + l.part.lsb;
#else
  n=(tUInt32) l.part.msb + l.part.lsb;
#endif

  *p=(tUInt32) (*p) + n;

  /* Returning the computed 16 bits integer */
  return *p;
}

